package cn.com.anysdk.map.service;

import cn.com.anysdk.map.config.AmapConfig;
import cn.com.anysdk.map.exception.MapApiException;
import cn.com.anysdk.map.exception.MapCoordinateException;
import cn.com.anysdk.map.model.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

/**
 * 高德地图服务实现
 */
@Slf4j
public class AmapService implements MapService {

    // 高德地图API基础URL
    private static final String BASE_URL = "https://restapi.amap.com/v3";
    // 地理编码API
    private static final String GEOCODING_URL = BASE_URL + "/geocode/geo";
    // 逆地理编码API
    private static final String REVERSE_GEOCODING_URL = BASE_URL + "/geocode/regeo";
    // 坐标转换API
    private static final String COORDINATE_CONVERT_URL = BASE_URL + "/assistant/coordinate/convert";
    // 静态地图API
    private static final String STATIC_MAP_URL = "https://restapi.amap.com/v3/staticmap";

    private final AmapConfig config;
    private final CloseableHttpClient httpClient;
    private final ObjectMapper objectMapper;

    public AmapService(AmapConfig config, CloseableHttpClient httpClient) {
        this.config = config;
        this.httpClient = httpClient;
        this.objectMapper = new ObjectMapper();
    }

    @Override
    public String getStaticMapUrl(String address, int zoom) {
        try {
            log.info("请求静态地图URL，address: {}, zoom: {}", address, zoom);
            // 先进行地理编码，获取坐标
            LocationResult locationResult = geocode(address);
            if (!locationResult.isSuccess()) {
                log.warn("地址解析失败: {}", locationResult.getErrorMessage());
                throw new MapApiException("地址解析失败: " + locationResult.getErrorMessage());
            }

            Location location = locationResult.getLocation();
            log.info("地理编码结果，location: {}", location);
            Map<String, String> params = new HashMap<>();
            params.put("location", location.getLongitude() + "," + location.getLatitude());
            params.put("zoom", String.valueOf(zoom));
            params.put("size", "800*600"); // 默认尺寸
            params.put("key", config.getAk());
            params.put("scale", "2"); // 高清图片

            URIBuilder builder = new URIBuilder(STATIC_MAP_URL);
            params.forEach(builder::addParameter);

            String url = builder.build().toString();
            log.info("生成的静态地图URL: {}", url);
            return url;
        } catch (Exception e) {
            log.error("生成静态地图URL失败, address: {}, zoom: {}", address, zoom, e);
            throw new MapApiException("生成静态地图URL失败: " + e.getMessage());
        }
    }

    /**
     * 地址转坐标（地理编码）
     *
     * @param address 地址
     * @return LocationResult
     */
    @Override
    public LocationResult geocode(String address) {
        try {
            log.info("请求地理编码, address: {}", address);
            Map<String, String> params = new HashMap<>();
            params.put("address", address);
            params.put("output", "json");
            params.put("key", config.getAk());

            JsonNode response = doGet(GEOCODING_URL, params);
            log.debug("地理编码API响应: {}", response);
            JsonNode geocodes = response.get("geocodes");
            
            if (geocodes == null || !geocodes.isArray() || geocodes.isEmpty()) {
                log.warn("地理编码失败, 响应: {}", response);
                return LocationResult.error("Geocoding failed: " + response.get("info").asText());
            }

            JsonNode location = geocodes.get(0).get("location");
            String[] latLng = location.asText().split(",");
            
            Location resultLocation = new Location();
            resultLocation.setLatitude(Double.parseDouble(latLng[1]));
            resultLocation.setLongitude(Double.parseDouble(latLng[0]));
            resultLocation.setCoordinateType(CoordinateType.GCJ02);

            log.info("地理编码成功, address: {}, location: {}", address, resultLocation);
            return LocationResult.success(resultLocation, address);
        } catch (Exception e) {
            log.error("Geocoding failed, address: {}", address, e);
            return LocationResult.error("Geocoding failed: " + e.getMessage());
        }
    }

    @Override
    public AddressResult reverseGeocode(Location location) {
        try {
            log.info("请求逆地理编码, location: {}", location);
            Map<String, String> params = new HashMap<>();
            params.put("location", location.getLongitude() + "," + location.getLatitude());
            params.put("output", "json");
            params.put("key", config.getAk());

            JsonNode response = doGet(REVERSE_GEOCODING_URL, params);
            log.debug("逆地理编码API响应: {}", response);
            JsonNode regeocode = response.get("regeocode");
            
            if (regeocode == null) {
                log.warn("逆地理编码失败, 响应: {}", response);
                return AddressResult.error("Reverse geocoding failed: " + response.get("info").asText());
            }

            String formattedAddress = regeocode.get("formatted_address").asText();
            log.info("逆地理编码成功, location: {}, address: {}", location, formattedAddress);
            return AddressResult.success(
                formattedAddress,
                location
            );
        } catch (Exception e) {
            log.error("Reverse geocoding failed, location: {}", location, e);
            return AddressResult.error("Reverse geocoding failed: " + e.getMessage());
        }
    }

    @Override
    public LocationResult convertCoordinate(Location location, CoordinateType fromType, CoordinateType toType) {
        try {
            log.info("请求坐标转换, location: {}, fromType: {}, toType: {}", location, fromType, toType);
            // 高德地图只支持 WGS84 到 GCJ02 的转换
            if (toType != CoordinateType.GCJ02) {
                log.warn("Amap only supports conversion to GCJ02 coordinate system, 当前toType: {}", toType);
                throw new MapCoordinateException("Amap only supports conversion to GCJ02 coordinate system");
            }

            Map<String, String> params = new HashMap<>();
            params.put("locations", location.getLongitude() + "," + location.getLatitude());
            params.put("coordsys", fromType == CoordinateType.WGS84 ? "gps" : "autonavi");
            params.put("output", "json");
            params.put("key", config.getAk());

            JsonNode response = doGet(COORDINATE_CONVERT_URL, params);
            log.debug("坐标转换API响应: {}", response);
            JsonNode locations = response.get("locations");
            
            if (locations == null || !locations.isArray() || locations.isEmpty()) {
                log.warn("坐标转换失败, 响应: {}", response);
                return LocationResult.error("Coordinate conversion failed: " + response.get("info").asText());
            }

            String[] latLng = locations.get(0).asText().split(",");
            Location convertedLocation = new Location();
            convertedLocation.setLatitude(Double.parseDouble(latLng[1]));
            convertedLocation.setLongitude(Double.parseDouble(latLng[0]));
            convertedLocation.setCoordinateType(CoordinateType.GCJ02);

            log.info("坐标转换成功, 原始: {}, 转换后: {}", location, convertedLocation);
            return LocationResult.success(convertedLocation, null);
        } catch (Exception e) {
            log.error("Coordinate conversion failed, location: {}, fromType: {}, toType: {}", location, fromType, toType, e);
            return LocationResult.error("Coordinate conversion failed: " + e.getMessage());
        }
    }

    @Override
    public MapProviderType getProviderType() {
        return MapProviderType.AMAP;
    }

    private JsonNode doGet(String url, Map<String, String> params) throws IOException, URISyntaxException, ParseException {
        URIBuilder builder = new URIBuilder(url);
        params.forEach(builder::addParameter);

        HttpGet request = new HttpGet(builder.build());
        request.setHeader("Accept", "application/json");

        log.debug("发起HTTP GET请求, url: {}, params: {}", url, params);
        try (var response = httpClient.execute(request)) {
            String responseBody = EntityUtils.toString(response.getEntity());
            log.debug("HTTP响应状态码: {}, 响应体: {}", response.getCode(), responseBody);
            JsonNode jsonResponse = objectMapper.readTree(responseBody);

            // 检查API返回状态码
            if (!"1".equals(jsonResponse.get("status").asText())) {
                log.error("Amap API error, url: {}, info: {}", url, jsonResponse.get("info").asText());
                throw new MapApiException(
                    "Amap API error: " + jsonResponse.get("info").asText(),
                    response.getCode()
                );
            }

            return jsonResponse;
        }
    }

}

